<?php

require_lib('net/Snoopy.class',false);

/** 
 * 传送拍拍接口[简称paipai]API参数到网店http://api.paipai.com/,得到相应返回值。
 * @author alex  
 * @version v1.0  2010-5-8
 */
class PaiPaiRequest {
	/**
	 * @var string $host_url paipai API的网站地址：http://api.paipai.com/
	 */
	var $_host_url='http://api.paipai.com/';
	
	/**
	 * @var array $app_token 应用app访问paipai的令牌，参数包括：<br/>
	 * string uin  操作用户QQ号码，用于认证操作用户的身份.<br/>
	 * string token:用于认证操作用户的身份.<br/>
	 * string spid:用于验证请求包是否被篡改.<br/>
	 * string skey:用户登录session[secretKey]，用于用户鉴权，将相关参数通过md5加密为一个sign值.
	 */
	var $app_token=array();
	
	
	/**
	 * @var array $app_setting 应用app访问paipai的通用参数设置，一般不改变默认值，包括：<br/>
	 * string charset：指定输入和输出的编码，paipai默认为 "gbk",charset只能作为URL参数提供"utf-8"，此处设为utf-8。<br/>
	 * string format：输出格式，"json"（默认），一般情况使用json。<br/>
	 * string pureData：是否只需要json数据，非0：只需要json数据，0（默认）：会多出变量定义、try…catch块之类
	 */
	var $app_setting=array('charset'=>'utf-8','format'=>'json','pureData'=>1);
	
	/**
	 *  @var string $url 上次调用API接口的URL.
	 */
	var $url;
	/**
	 * @var int HTTPMETHOD_POST $httpmethod使用POST方法传递API参数，$httpmethod默认使用HTTPMETHOD_POST
	 */ 
	const  HTTPMETHOD_POST=0; 
	/**
	 * @var int HTTPMETHOD_POST $httpmethod使用GET方法传递API参数
	 */ 	
	const  HTTPMETHOD_GET=1;
	/**
	 * @var int HTTPMETHOD_POSTWITHFILE $httpmethod使用POST方法传递API参数，并且还要上传文件<br/>
	 * 如上传图像，array('image' => '/tmp/cs.jpg')
	 */ 		
	const  HTTPMETHOD_POSTWITHFILE=2;
	/**
	 * 
	 * @var int $httpmethod 传递API参数的方法，其值是：@see HTTPMETHOD_POST,@see HTTPMETHOD_GET,@see HTTPMETHOD_POSTWITHFILE <br/>
	 * 默认为HTTPMETHOD_POST，如果参数不合法，使用HTTPMETHOD_POST。
	 */
	var $httpmethod=0;
	/**
	 * @var array $postWithFiles 当$httpmethod=HTTPMETHOD_POSTWITHFILE时，使用此参数设置的文件上传<br/>
	 * 如上传图像，array('image' => '/tmp/cs.jpg')
	 */
	var $postWithFiles=array();
    function __construct(){
    	$this->httpmethod=self::HTTPMETHOD_GET;
    }		
	/**
	 * 得到paipai API的返回值，JSON字符串。
	 * @param string $cmdid 要调用的api接口的cmdid字符串，如user.getUserInfo，如果为空，将返回false.
	 * @param array $paramArr 要调用的api接口的参数数组，一般通用的参数不要传，如charset，format等。
	 * @return string|bool 调用的api接口得到的JSON字符串。
	 */
	function &get($cmdid,array $paramArr){
		if(! $cmdid) return false;
		
		//组装url,method GET.
		$cmdurl=str_replace('.','/',$cmdid).'.xhtml';
		$token=$this->app_token;
		unset($token['skey']);
		$paramArr=$paramArr + $this->app_setting;
		$paramArr=$paramArr+$token;
		$sign = $this->_createSign($cmdid,$paramArr);
		$this->url =$this->_host_url.$cmdurl;

 		//call taobao method
        $snoopy = new Snoopy();
        $snoopy->accept='text/json';
        if($this->httpmethod == self::HTTPMETHOD_GET){
          $this->url .= '?'.$this->_createStrParam($paramArr);
          if($sign)  $this->url .='sign='.$sign;      
          $snoopy->fetch($this->url);
          if($snoopy->error)  throw new Exception($snoopy->error);
          return $snoopy->results;          
        }
        elseif ($this->httpmethod== self::HTTPMETHOD_POSTWITHFILE 
               && count($this->postWithFiles)>0){
          if($sign) $paramArr['sign']=$sign;      	
          $snoopy->_submit_type='multipart/form-data';
          $snoopy->submit($this->url,$this->_prepareParam($paramArr),$this->postWithFiles);
          if($snoopy->error)  throw new Exception($snoopy->error);
          //paipai bug,return gbk
           for($i=0;$i<count($snoopy->headers);$i++){
          	 if(strpos($snoopy->headers[$i],'charset=gbk')!==false)
          	   return iconv('gbk','utf-8',$snoopy->results);
          }
          return $snoopy->results;         
        }
        else{ 
          if($sign) $paramArr['sign']=$sign;
          $snoopy->submit($this->url,$this->_prepareParam($paramArr));
          if($snoopy->error)  throw new Exception($snoopy->error);
          
          //paipai bug,return gbk
          for($i=0;$i<count($snoopy->headers);$i++){
          	 if(strpos($snoopy->headers[$i],'charset=gbk')!==false)
          	   return iconv('gbk','utf-8',$snoopy->results);
          }
          return $snoopy->results;
        }
	}
	
	/**
	 * 得到paipai API的返回值，JSON生成的数组。
	 * @param string $cmdid 要调用的api接口的cmdid字符串，如user.getUserInfo，如果为空，将返回false.
	 * @param array $paramArr 要调用的api接口的参数数组，一般通用的参数不要传，如charset，format等。
	 * @param string $handleGetResultFunc 用于额外处理get函数返回字符串的函数名称，此函数调用在调用get函数后，解析为数组前，默认为NULL不调用。
	 * @return array|bool 调用的api接口得到的 JSON生成的数组。
	 */	
	function &getArray($cmdid,array &$paramArr,$handleGetResultFunc=NULL){
		if(!($result=&$this->get($cmdid,$paramArr))) return false;
		if($handleGetResultFunc)  //addition handle result 
		   try{$result = $handleGetResultFunc($result);}catch (Exception  $e){}		
		if (version_compare(PHP_VERSION,'5.3.0')<0) 
		   $result = preg_replace('/(":)(\d{9,})/i', '${1}"${2}"', $result);		
		$arr=json_decode($result,true);
		if ($arr == NULL) {
			//for paipai normal handle
			$patterns = array ('/,+\s*\}/', '/,+\s*\]/', '/\\\{1,3}/','/"\s+|\s+"/' );
			$replacements = array ('}', ']', '\\\\\\\\','"');
			$result = preg_replace ( $patterns, $replacements, $result );

			$arr = json_decode ( $result, true );
		}
		return $arr;
	}	
	
	/**
	 * 得到paipai API的返回值，JSON生成的stdclass对象。
	 * @param string $cmdid  要调用的api接口的cmdid字符串，如user.getUserInfo，如果为空，将返回false.
	 * @param array $paramArr 要调用的api接口的参数数组，一般通用的参数不要传，如charset，format等。
	 * @return object|bool 调用的api接口得到的JSON生成的stdclass对象。
	 */
	/*
	function getObj($cmdid,array &$paramArr){
		if(!($result=&$this->get($cmdid,$paramArr))) return false;
		return json_decode($result,false);
	}	
	*/
	/**
	 * 检查接口调用get*函数返回是否有错误
	 * @param  mixed $result  接口调用get*函数返回值.
	 * @return bool true 接口调用有效，false 接口调用出错.
	 */
	function checkResult(&$result){
		if(is_array($result)){
			if(array_key_exists('errorCode',$result) && $result['errorCode'] == 0)
			  return true;
		}elseif (is_object($result)){
			if(property_exists($result,'errorCode') && $result->errorCode == 0)
			   return true;
		}
		elseif (is_string($result)){
			$match=array();
			if(preg_match('/".*errorCode.*:\d/',$result,$match)){
			  $m=preg_split('/:/',$match[0]);
			  if(count($m)>1 && $m[1] == 0)
			     return true;
			}
		}
		elseif(is_bool($result))
		 return $result;
		 
		return false;
	}
	
	/*
	 * 组参函数 ,内部用
	 */
	private function _createStrParam(&$paramArr) {
		$strParam = '';
		foreach ( $paramArr as $key => $val ) {
			if ($key != '' && isset($val)) {
				if(is_array($val)) {
					if( count($val)>0)
					foreach ($val as $item)
					  $strParam .= $key . '=' . urlencode( $item ) . '&';
				}
				else 
				$strParam .= $key . '=' . urlencode( $val ) . '&';
			}
		}
		return $strParam;
	}	
	private function _prepareParam(array &$paramArr) { //trim empty value
		$Paramok =array();
		foreach ( $paramArr as $key => $val ) {
			if ($key != '' && isset($val)) {
				if(is_array($val)) {
					$itemarr=array();
					foreach ($val as $item)
					  if($item != '')  $itemarr[]=$item;
					$Paramok[$key] = $itemarr ;
				}
				else 
				$Paramok[$key] = $val; //Snoopy类在执行submit函数时，它自动会将参数做urlencode编码
			}
		}
		return $Paramok;
	}	
	/*
	 * 签名函数,内部用 
	 */
    private function _createSign($cmdid, $paramArr) {
		
		$skey=$this->app_token['skey'];
		if(!$skey) return NULL;
		
		$sign = $cmdid;
		ksort ( $paramArr );
		foreach ( $paramArr as $key => $val ) {
			if ($key != '' &&  isset($val) ) {
				if (is_array ( $val )) {
					if(count ( $val ) > 0)
					foreach ( $val as $item )
						$sign .= $key . $item;
				} 
				else
					$sign .= $key . $val;
			}
		}	
		$sign = md5($sign . $skey);
		return $sign;
	}	
}

